import maya.cmds as cmds
import imageryonline, cPickle, base64, time, sys, re, os

# Created by Jason Dixon 7/05/14
# http://internetimagery.com

#### Information about script!
def About(version):
	message = """
Message Board Script.
Created by Jason Dixon.
http://internetimagery.com"""
	title = "Version "+version
	cmds.confirmDialog(t=title, m=message)

### Help for markdown syntax
def Markdown():
	text = """
<h1>#Title Big</h1>
<h2>##Title Medium</h2>
<h3>###Title Small</h3>
<em>*itallic*</em><br/><br/>
<strong>**bold**</strong><br/><br/>
* List<br/><br/>
[link](website)<br/>
<a href="#">Example</a><br/><br/>
***  Line Break<hr><br/>
<code>```<br/>
Code<br/>
```</code>
"""
	cmds.confirmDialog(t='Markdown Syntax', m=text)

#### Dock section and dropdown menu of the GUI
class GUI(object):
	#__shared_state = {}
	def __init__(self):
		#self.__dict__ = self.__shared_state
		self.version = '1.0'
		self.GUI = {} #GUI stuffs
		self.connection = imageryonline.Connect()
		self.web = '/messageboard/'
		self.boards = [] #list of availible boards
		self.page = 0 #page number

		#### load preferences
		load = Prefs()
		self.prefs = {}
		self.prefs['dock_area'] = load.Get('dock_area') if load.Get('dock_area') else 'float' #dock area
		self.prefs['active_board'] = load.Get('active_board') #active board
		self.prefs['display'] = load.Get('display') if load.Get('display') else 'day' #range of posts to view

		self.GUI['window']= cmds.window( title = 'Message Board', rtf=True, mb=True)
		self.GUI['menu1'] = cmds.menu( l='Action', pmc=self.update_log_button )
		self.GUI['drop1'] = cmds.menuItem( l='Add User'	 , c=Run(self.add_user))
		self.GUI['drop2'] = cmds.menuItem( l='List Users' , c=Run(self.list_users))
		self.GUI['drop10'] = cmds.menuItem( l='Report Users')
		self.GUI['drop5'] = cmds.menuItem( divider=True )
		self.GUI['drop3'] = cmds.menuItem( l='Create Board', c=Run(self.add_board))
		self.GUI['drop4'] = cmds.menuItem( l='Leave Board', c=Run(self.leave_board))
		self.GUI['drop5'] = cmds.menuItem( divider=True )
		self.GUI['drop97']= cmds.menuItem( l='Markdown Syntax', c=Run(Markdown) )
		self.GUI['drop99']= cmds.menuItem( l='Logging' )
		self.GUI['drop98']= cmds.menuItem( l='About', c=Run(About, self.version) )
		self.GUI['drop95']= cmds.menuItem( l='Check Updates', c=Run(self.Update_Check) )

		self.GUI['menu2'] = cmds.menu( l='Board' )

		self.GUI['menu3'] = cmds.menu( l='View' )
		self.GUI['drop6'] = cmds.menuItem( l='Hour' , c=Run(self.change_display, 'hour') )
		self.GUI['drop7'] = cmds.menuItem( l='Day'  , c=Run(self.change_display, 'day') )
		self.GUI['drop8'] = cmds.menuItem( l='Week' , c=Run(self.change_display, 'week') )
		self.GUI['drop9'] = cmds.menuItem( l='Month', c=Run(self.change_display, 'month') )
		self.GUI['drop10'] = cmds.menuItem( l='All'  , c=Run(self.change_display, 'all') )

		self.GUI['layout1']=cmds.columnLayout( adjustableColumn=True, en=False )
		self.GUI['text1']	 = cmds.text(h=25,l='Please Log in to read messages.')
		self.GUI['layout2']=cmds.rowLayout(nc=3,adj=2)
		self.GUI['button1']=cmds.button(l='<- Prev ', c=Run(self.change_page,1))
		self.GUI['button2']=cmds.button(l='REFRESH' , c=Run(self.refresh_all)	 )
		self.GUI['button3']=cmds.button(l=' Next ->', c=Run(self.change_page,-1) ,en=False)
		cmds.setParent('..') #row layout
		self.GUI['layout3']=cmds.scrollLayout(cr=True, h=300)
		self.GUI['layout4']=cmds.columnLayout( adjustableColumn=True, bgc=(0.25,0.25,0.25)) # messages go in here!
		cmds.setParent('..') #column layout
		cmds.setParent('..') #scroll layout

		self.GUI['input2'] =cmds.scrollField(ww=True, h=60)
		self.GUI['button4']=cmds.button(l='Post Message', c=Run(self.add_post))
		allowed_areas = ['right', 'left']
		self.GUI['dock'] = cmds.dockControl(a='left',con=self.GUI['window'],aa=allowed_areas,l='Message Board', fcc=self.move_dock,vcc=self.close_event)#,epo=True)

		if self.prefs['dock_area'] == 'float':
			cmds.dockControl(self.GUI['dock'],e=True,fl=True)
		else:
			cmds.dockControl(self.GUI['dock'],e=True,a=self.prefs['dock_area'])

		Login(self.connection, self.refresh_all, True)

	def Update_Check(self):
		note, result = self.connection.send('/version/maya/messageboard')
		if result:
			message = "You are up to date." if note['version'] == self.version else "A new version is available.\n\n You can download it at '"+note['download']+"'."
		else:
			print note
			message = "There was an issue checking for updates."
		cmds.confirmDialog(t='Update Check', m=message)


	def move_dock(self): #save location of the dock when moved to new location
		if cmds.dockControl(self.GUI['dock'],q=True,fl=True):
			area = 'float'
		else:
			area = cmds.dockControl(self.GUI['dock'],q=True,a=True)
		self.prefs['dock_area'] = area

	def update_log_button(self, *button): #dynamically change the logout button to login if not currently logged in.
		if self.connection.status:
			cmds.menuItem(self.GUI['drop99'],e=True,l='Log out..',c=self.server_disconnect )
		else:
			cmds.menuItem(self.GUI['drop99'],e=True,l='Log in..',c=self.server_connect )

	def change_display(self, display): #change display choice
		self.prefs['display'] = display
		self.page = 0
		self.update_posts()

	def change_board(self, board): #change board selected
		self.prefs['active_board'] = board
		self.page = 0
		self.update_posts()

	def change_page(self, change): #flip through pages
		self.page += change
		self.update_posts()

	def add_post(self):
		if self.connection.status:
			message = cmds.scrollField(self.GUI['input2'],tx=True,q=True).strip()
			if message:
				print 'Submitting Message'
				post_info = {'board':self.prefs['active_board'],'post_message':message}
				note, result = self.connection.send(self.web+'new_post', post_info)
				if result:
					#time.sleep(0.8)
					self.update_posts()
					cmds.scrollField(self.GUI['input2'],tx='',e=True)
				else:
					print 'ERROR:',note
			else:
				cmds.text(self.GUI['text1'],e=True,l='Message and Link Empty.')

	def add_user(self):
		if self.connection.status:
			if cmds.promptDialog(t='User Name',m='Enter the name of a user to add them to your Board.') == 'Confirm':
				text = cmds.promptDialog(query=True, text=True).strip()
				if text: #username checks out?
					web_url = self.web+'access'
					post_info = {'board':self.prefs['active_board'],'user':text}
					note, result = self.connection.send(web_url,post_info)
					if result:
						message = '%s successfully added to %s.' %(text, self.prefs['active_board'])
						print message
						cmds.text(self.GUI['text1'],e=True,l=message)
						return
					else:
						cmds.confirmDialog(t='Oh no!',m="Could not add user. Check the username is correct and the user doesn't already have access.")
			cmds.text(self.GUI['text1'],e=True,l='Enter a Username.')

	def add_board(self):
		if self.connection.status:
			if cmds.promptDialog(t='Board Name',m="Enter the name you wish to give to your board.\nUse only letters/numbers/spaces.") == 'Confirm':
				text = cmds.promptDialog(query=True, text=True).strip().replace(' ','_')
				if text: #boardname checks out?
					post_info = {'board':text }
					note, result = self.connection.send(self.web+'new_board', post_info)
					if result:
						message = '%s board created successfully.' % text
						print message
						cmds.text(self.GUI['text1'],e=True,l=message)
						self.prefs['active_board'] = text
						self.refresh_all()
					else:
						cmds.confirmDialog(t='Oh no!',m=note)

	def leave_board(self):
		if self.connection.status:
			mess = 'Are you sure you want to leave the board %s?\nIf you want back in, you will need to be invited.' % self.prefs['active_board']
			if cmds.confirmDialog(t='OooOooh',m=mess) == 'Confirm':
				if self.prefs['active_board'] in self.boards:
					post_info = {'board':self.prefs['active_board']}
					web_url = self.web+'leave'
					note, result = self.connection.send(web_url, post_info)
					if result:
						message = 'You have left: %s.' % self.prefs['active_board']
						print message
						cmds.text(self.GUI['text1'],e=True,l=message)
						self.refresh_all()
					else:
						cmds.confirmDialog(t='Oh no!',m=note)

	def list_users(self):
		if self.connection.status:
			post_info = {'board': self.prefs['active_board']}
			data, result = self.connection.send(self.web+'user_list' , post_info)
			if result and data:
				data = data['user_list']
				win = cmds.window( title ='Users', rtf=True, s=False)
				cmds.columnLayout( adjustableColumn=True )
				cmds.text(l='### Users in '+self.prefs['active_board'].upper()+' ###')
				cmds.setParent('..')
				width = len(data)/2 if len(data) < 10 else 10
				cmds.gridLayout(cwh=(100,30),nc=width if width > 0 else 1)
				for post in data:
					cmds.text(l='  '+post)
				cmds.setParent('..')
				cmds.showWindow( win )

			else:
				print 'ERROR:',data
				cmds.text(self.GUI['text1'],e=True,l='There was a problem retrieving user list.')

	def update_board_menu(self): #build board menu
		if self.connection.status:
			existing = cmds.menu(self.GUI['menu2'],q=True,ia=True)
			if existing:
				for ui in existing:
					if cmds.menuItem(ui, ex=True):
						cmds.deleteUI(ui)
			for board in self.boards:
				nice_board = board.replace('_',' ')
				cmds.menuItem( l=nice_board, p=self.GUI['menu2'], c=Run(self.change_board, board) )

	def update_boards(self): #grab list of boards
		if self.connection.status:
			data, result = self.connection.send(self.web+'boards')
			if result:
				self.boards = data['boards']
			else:
				self.boards = []
				print "ERROR:",data
		else:
			self.boards = []

	def update_posts(self): #update GUI with new posts
		if self.connection.status and self.boards:
			if not self.prefs['active_board'] in self.boards: #if last viewed board still is accessible, go there
				self.prefs['active_board'] = self.boards[0]
			print 'Updating Posts',self.prefs['active_board']
			post_info = {}
			post_info['board']   = self.prefs['active_board']
			post_info['display'] = self.prefs['display']
			post_info['page']    = self.page
			data, result = self.connection.send(self.web+'posts', post_info)
			if result:
				existing = cmds.layout(self.GUI['layout4'],q=True,ca=True)
				if existing:
					for ui in existing:
						if cmds.layout(ui, ex=True):
							cmds.deleteUI(ui, lay=True)
				for (i,post) in enumerate(data['posts']):
					colour = [0.3,0.3,0.3] if (i+1)%2 else [0.35,0.35,0.35]
					parent = cmds.columnLayout( adjustableColumn=True, p=self.GUI['layout4'], bgc=colour ) #hold post
					cmds.text(l=post['user'], p=parent, al='right', hl=True, fn='smallObliqueLabelFont') #username
					cmds.text(l=post['post'], p=parent, al='left', ww=True, hl=True, fn='fixedWidthFont')#message
					cmds.text(l=post['published'], p=parent, al='right', fn='smallObliqueLabelFont') #time passed since publish
				title = 'Viewing the board %s from ' % self.prefs['active_board'].replace('_',' ').upper()
				if self.prefs['display'] == 'all':
					title += 'all time.'
					cmds.button(self.GUI['button1'],e=True,en=False)
					cmds.button(self.GUI['button3'],e=True,en=False)
				elif self.page > 0:
					title += '%s %s%s ago.' %(self.page, self.prefs['display'], 's' if self.page > 1 else '')
					cmds.button(self.GUI['button1'],e=True,en=True)
					cmds.button(self.GUI['button3'],e=True,en=True)
				else:
					title += 'the current %s.' % self.prefs['display']
					cmds.button(self.GUI['button1'],e=True,en=True)
					cmds.button(self.GUI['button3'],e=True,en=False)
					self.page = 0
				cmds.text(self.GUI['text1'],e=True,l=title)
				cmds.dockControl(self.GUI['dock'],e=True,l='Browsing: '+self.prefs['active_board'].upper())
			else:
				print 'ERROR:',data

	def refresh_all(self): #refresh entire GUI (ie when logged in)
		if self.connection.status: #make sure we are logged in...
			self.update_boards() #update list of avaibile boards
			if self.boards: #make sure we have boards... otherwise... we might be banned. whoops...
				self.update_board_menu() #update actual board selection menu
				self.update_posts() #grab posts
				cmds.columnLayout(self.GUI['layout1'],e=True, en=True ) #unlock GUI interface
		else:
			cmds.columnLayout(self.GUI['layout1'],e=True, en=False )

	def close_event(self,*loop): #Remove dock window when closed
		vis = cmds.dockControl(self.GUI['dock'],q=True,vis=True)
		if not vis and loop:
			cmds.scriptJob(ie=self.close_event,p=self.GUI['dock'],ro=True)
		elif not vis:
			cmds.deleteUI(self.GUI['dock'],control=True)
			self.connection = None
			self.save_prefs()

	def server_connect(self,button): #open login window
		Login(self.connection,self.refresh_all)

	def server_disconnect(self,button): #logout
		note, result = self.connection.logout()
		if result:
			print 'Logged out. Bye bye.'
		self.refresh_all()

	def save_prefs(self):
		print 'Saving Preferences.'
		save = Prefs()
		for key in self.prefs:
			save.Put(key, self.prefs[key])

	def __del__(self):
		pass

#### Preferences
class Prefs(object):
	def __init__(self):
		self.filename = os.path.join(os.path.split(__file__)[0], 'message_board.prefs')
		self.info = {} #info saved
		try:
			pref = open(self.filename, 'rb')
			self.info = cPickle.loads( base64.b64decode(pref.read()) )
		except (IOError, EOFError):
			pref = open(self.filename, 'wb')
		pref.close()
	def Get(self, key=None): #retrieve pref info
		return self.info.get(key, None) if key else self.info
	def Put(self, key, val): #insert pref info
		self.info[key] = val
	def __del__(self):
		with open(self.filename, 'wb') as pref:#base64.b64encode
			pref.write( base64.b64encode(cPickle.dumps(self.info)) )

#### Login window, login to server
class Login(object): #login window
	#__shared_state = {}
	def __init__(self, connection, callback = None, auto = False):
		#self.__dict__ = self.__shared_state
		#DATA
		self.GUI = {} #gui elements
		self.online = connection #connection to server
		self.callback = callback #called when login status changes
		self.passwords = {}

		self.GUI['window']  = cmds.window( title = 'Internet Imagery Login...', rtf=True, s=False)
		self.GUI['layout1'] = cmds.frameLayout( l="Login" )
		self.GUI['text1']	= cmds.text( l = 'Hey ladies.', h=30)
		self.GUI['input1']  = cmds.textFieldGrp( l='Username / Email' )
		self.GUI['input2']  = cmds.textFieldGrp( l='Password', tcc=Run(self.hide_password, 'input2'))
		self.GUI['input3']  = cmds.checkBoxGrp( l='Remember Details' )
		self.GUI['button1'] = cmds.button( l= 'Login', h=30, c=self.login )
		cmds.setParent('..')
		self.GUI['layout2'] = cmds.frameLayout( l="Don't have a login? Create one HERE:", cll = True, cl= True )
		self.GUI['input4']  = cmds.textFieldGrp( l='Username (2-64 chars)' )
		self.GUI['input5']  = cmds.textFieldGrp( l='Email' )
		self.GUI['input6']  = cmds.textFieldGrp( l='Password (min 6 chars)',tcc=Run(self.hide_password, 'input6'))
		self.GUI['input7']  = cmds.textFieldGrp( l='Repeat Password',tcc=Run(self.hide_password, 'input7'))
		self.GUI['button2'] = cmds.button( l = 'Register new user', h=30, c=self.register )
		cmds.setParent('..')
		cmds.showWindow( self.GUI['window'] )

		self.message()
		load = Prefs()
		cmds.textFieldGrp(self.GUI['input1'],e=True,tx=load.Get('login:input1') if load.Get('login:input1') else '')
		if load.Get('login:input3'):
			self.passwords['input2'] = load.Get('login:input2')
			cmds.textFieldGrp(self.GUI['input2'],e=True,tx='*'*len(load.Get('login:input2')))
			cmds.checkBoxGrp( self.GUI['input3'],e=True,v1=load.Get('login:input3'))
			if auto:
				self.message('Logging in...')
				cmds.refresh( cv = True )
				self.login('KAPOW')

	def hide_password(self, key, *button): #hide the password behind asterixes (does mean you cannot use asterixes in passwords)
		new_pass = cmds.textFieldGrp(self.GUI[key],q=True,tx=True)#new
		self.passwords[key] = self.passwords.get(key,'')#old
		join_pass = ''
		i=0
		if len(new_pass) >= len(self.passwords[key]):
			for char in new_pass.split('*'):
				if char:
					join_pass+=char
				else:
					try:
						join_pass+=self.passwords[key][i]
						i+=1
					except IndexError:
						pass
		self.passwords[key] = join_pass
		cmds.textFieldGrp(self.GUI[key],e=True,tx='*'*len(join_pass))

	def message(self, mess='Enter Login Details...'): #helpful message
		cmds.text(self.GUI['text1'], l=mess, e=True)

	def login(self, button):
		user = cmds.textFieldGrp(self.GUI['input1'],q=True,tx=True).strip()
		pasw = self.passwords['input2'].strip()
		check= cmds.checkBoxGrp(self.GUI['input3'],q=True,v1=True)
		save = Prefs()
		save.Put('login:input1',user)
		if not check:
			save.Put('login:input3',None)
			save.Put('login:input2',None)
		if user:
			if pasw:
				note, result = self.online.login(user, pasw)
				if result:
					self.message("You have logged in.") #logged in
					if cmds.checkBoxGrp(self.GUI['input3'],q=True,v1=True):
						save.Put('login:input3',True) #store checkbox status
						save.Put('login:input2',pasw) #store password base64 to hide characters
					print 'Logged in successfully.'
					self.close_win()
				else:
					print 'ERROR:',note
					self.message('There was a problem trying to log in.')
			else:
				self.message('You need to enter a password.')
		else:
			self.message('You need to enter a username.')

	def register(self, button):
		user	= cmds.textFieldGrp(self.GUI['input4'],q=True,tx=True).strip()
		email = cmds.textFieldGrp(self.GUI['input5'],q=True,tx=True).strip()
		pasw1 = self.passwords['input6'].strip()
		pasw2 = self.passwords['input7'].strip()
		user_len = len(user)
		pasw_len = len(pasw1)
		if user and 1 < user_len < 65:
			if pasw1 and 5 < pasw_len:
				if pasw1 == pasw2:
					if email and re.match("[^@]+@[^@]+\.[^@]+", email):
						note, result = self.online.register(user, email, pasw1, pasw2)
						if result: #registered successfully
							self.message('Successfully registered.')
							note, result = self.online.login(user, pasw1)
							if result:
								print 'Successfully registered and logged in.'
								self.close_win()
							else:
								self.message(note)
						else:
							self.message(note)
					else:
						self.message('Please insert a valid email address.')
				else:
					self.message('Your passwords do not match.')
			else:
				self.message('You need to enter a password of 6 characters or more.')
		else:
			self.message('You need to enter a username, between 2 and 64 characters.')
	def close_win(self):
		if cmds.window(self.GUI['window'],ex=True):
			sfx_height = cmds.window(self.GUI['window'],h=True,q=True)
			for i in range(10): #shrink for coolness factor! Ooooh yeah!
				   i = sfx_height - i*(sfx_height*0.1)
				   cmds.window(self.GUI['window'],h=i,e=True)
				   cmds.refresh( cv = True )
			cmds.deleteUI(self.GUI['window'],wnd=True)
			if self.callback:
				self.callback()

#### Run for buttons
class Run(object): #generic callback for buttons to pass values
		def __init__(self, func, *args, **kwargs):
				self.func = func
				self.args = args
				self.kwargs = kwargs
		def __call__(self, *args):
				return self.func( *self.args, **self.kwargs )

